package com.cmii.sjw.department.control.jsc.util;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class ApiCaller {

    private static final Logger log = LoggerFactory.getLogger(ApiCaller.class);

    private static final String TOKEN_URL = "https://drs.dsjfzj.cq.cegn:11007/apisix/token";

    private Map<String, TokenResult> cachedTokenMap = new HashMap<>();

    public static void main(String[] args) {
        try {
            String accessKey = "BCDSGA_0a077954507531276ce49e5f3a4ea2c2";
            String secretKey = "BCDSGS_b768e428a8438003f2b618ed021ed61e";
            ApiCaller apiCaller = new ApiCaller();
            String url = "https://drs.dsjfzj.cq.cegn:11007/restapi/prod/IC50000020240415000054/xdata/tbl/api/execute/50d2d672-b85f-4c71-9731-37e05398c3b8";
            String method = "POST";
            String token = apiCaller.getToken(accessKey, secretKey);
            Map<String, String> param = new HashMap<>();
            String result = apiCaller.callUrl(url, method, token, param);
            log.info("call result:{}", result);
        } catch (Exception e) {
            log.error("call failed", e);
        }
    }

    /**
     * 调用并返回业务类型结果
     * @param url 网关地址
     * @param method url方法
     * @param token 网关token
     * @param param 业务封装参数
     * @param type 业务返回结果类型
     * @return
     * @param <T>
     * @param <R>
     * @throws Exception
     */
    public <T, R>  R callUrl(String url, String method, String token, T param, Class<R> type) throws Exception {
        String json = callUrl(url, method, token, param);
        if (StringUtils.isEmpty(json)) {
            return null;
        }
        return JSONObject.parseObject(json, type);
    }

    /**
     * 调用网关接口
     * @param url 接口url
     * @param token token信息
     * @param param 业务参数
     * @return 网关结果
     * @param <T>
     */
    public <T> String callUrl(String url, String method, String token, T param) throws Exception {
        HttpClient httpClient = HttpClientBuilder.create().build();
        RequestBuilder requestBuilder = RequestBuilder.create(method)
                .addHeader("X-ACCESS-TOKEN", token)
                .addHeader("Content-Type", "application/json")
                .setUri(new URIBuilder(url).build());
        if (param != null && !"GET".equalsIgnoreCase(method)) {
            requestBuilder.setEntity(new StringEntity(JSONObject.toJSONString(param), StandardCharsets.UTF_8));
        }
        HttpResponse response = httpClient.execute(requestBuilder.build());
        if (response.getStatusLine().getStatusCode() != 200) {
            log.error("call url:{}, failed, error:{}", url, EntityUtils.toString(response.getEntity()));
            throw new RuntimeException("execute failed");
        }
        return EntityUtils.toString(response.getEntity());
    }

    /**
     * 缓存token信息, token失效重新获取
     * @return token信息
     * @throws Exception
     */
    public String getToken(String accessKey, String secretKey) throws Exception{
        TokenResult cachedToken = cachedTokenMap.get(accessKey);
        if (cachedToken != null && StringUtils.isNotEmpty(cachedToken.getToken())
        &&  cachedToken.getExpire_time() > (System.currentTimeMillis() / 1000) + 60) {
            return cachedToken.getToken();
        }
        TokenResult tokenResult = doGetToken(accessKey, secretKey);
        cachedTokenMap.put(accessKey, tokenResult);
        return tokenResult.getToken();
    }

    /**
     * 通过http调用获取token信息
     * @return token信息
     * @throws Exception
     */
    private TokenResult doGetToken(String accessKey, String secretKey) throws Exception {
        HttpClient httpClient = HttpClientBuilder.create().build();
        long timestamp = System.currentTimeMillis() / 1000;
        //按照字典对参数进行排序
        Map<String, String> params = new TreeMap<>();
        params.put("access_key", accessKey);
        params.put("timestamp", String.valueOf(timestamp));
        String signString = params.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
        //对参数进行加密
        String signature = HmacUtils.hmacSha256Hex(secretKey, signString);
        //获取动态token
        URIBuilder uriBuilder = new URIBuilder(TOKEN_URL);
        uriBuilder.addParameter("access_key", accessKey);
        uriBuilder.addParameter("timestamp", String.valueOf(timestamp));
        uriBuilder.addParameter("signature", signature);
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        HttpResponse response = httpClient.execute(httpGet);
        String responseBody = EntityUtils.toString(response.getEntity());
        if(response.getStatusLine().getStatusCode() != 200) {
            throw new RuntimeException("get token failed, error msg: " + responseBody);
        }
        TokenResult tokenResult = JSONObject.parseObject(responseBody, TokenResult.class);
        if (StringUtils.isNotEmpty(tokenResult.getError_msg())) {
            throw new RuntimeException(tokenResult.getError_msg());
        }
        return tokenResult;
    }

    /**
     * 加密字段解密
     * @param encrypt 加密字段
     * @param secretKey
     * @return 解密结果
     * @throws Exception
     */
    public String decryptField(String encrypt, String secretKey) throws Exception {
        if (StringUtils.isEmpty(encrypt)) {
            return encrypt;
        }
        //对密码进行编码, key为sk后16位字符，加解密方式为aes cbc
        String aesKey = secretKey.substring(secretKey.length() - 16);
        byte[] bytes = aesKey.getBytes(StandardCharsets.UTF_8);
        //设置解密算法，生成秘钥
        SecretKeySpec skeySpec = new SecretKeySpec(bytes, "AES");
        //偏移
        IvParameterSpec iv = new IvParameterSpec(aesKey.getBytes(StandardCharsets.UTF_8));
        // "算法/模式/补码方式"
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //选择解密
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        //先进行Base64解码
        byte[] decodeBase64 = Base64.getDecoder().decode(encrypt.getBytes(StandardCharsets.UTF_8));
        //根据待解密内容进行解密
        byte[] decrypted = cipher.doFinal(decodeBase64);
        //将字节数组转成字符串
        String decrypt =  new String(decrypted, StandardCharsets.UTF_8);
        return decrypt;
    }

    public static class TokenResult {

        /**
         * token信息
         */
        private String token;

        /**
         * token过期时间(单位为秒级时间戳)
         */
        private long expire_time;

        /**
         * 网关返回错误消息
         */
        private String error_msg;

        public String getToken() {
            return this.token;
        }

        public void setToken(String token) {
            this.token = token;
        }

        public long getExpire_time() {
            return this.expire_time;
        }

        public void setExpire_time(long expire_time) {
            this.expire_time = expire_time;
        }

        public String getError_msg() {
            return this.error_msg;
        }

        public void setError_msg(String error_msg) {
            this.error_msg = error_msg;
        }
    }
}